伺服器代表使用者或應用程式本身發起請求是很常見的功能。以下是一些具體的例子:
這些功能都涉及伺服器代表使用者或應用程式本身發起請求,而如果沒有適當的安全措施,這些功能可能會成為 SSRF 攻擊的潛在目標。
因此,在實作這些功能時,確保對所有外部請求進行嚴格的驗證和控制是非常重要的。但如果沒有適當的安全措施,可能會被惡意利用,導致 SSRF(Server-Side Request Forgery,伺服器端請求偽造)攻擊。
SSRF 攻擊允許攻擊者從目標伺服器發起未經授權的請求,這可能導致敏感資訊洩露、內部系統存取,甚至遠端程式碼執行。
本文將深入探討 SSRF 的概念、風險,以及在 Node.js 環境中的具體實作和防禦措施。
SSRF 是一種安全漏洞,攻擊者能夠利用易受攻擊的伺服器作為代理,發送惡意請求。這些請求可能針對內部網路資源、外部系統,或者伺服器本身。
在正常情況下,伺服器可能會代表使用者取得資源,例如:
const axios = require('axios');
app.get('/fetch-resource', async (req, res) => {
const { url } = req.query;
const response = await axios.get(url);
res.json(response.data);
});
然而,如果沒有適當的驗證和限制,攻擊者可能會濫用此功能存取非預期的資源。
在本次實作中,我們將建立一個易受 SSRF(Server-Side Request Forgery,伺服器端請求偽造)攻擊的環境,並設定一個內部 API 來模擬敏感資料。這將幫助我們更好地理解 SSRF 漏洞的風險和防禦策略。
https://github.com/fei3363/ithelp_web_security_2024/commit/25e04fa7eed55743621c94de14dd61932e42deb1
首先,我們需要在 docker-compose.yml
檔案中新增 internal-api
服務:
services:
app:
# ... 其他設定 ...
depends_on:
- mongo
- postgres
- internal-api
internal-api:
build:
context: ./internal-api
dockerfile: Dockerfile
environment:
- SECRET_KEY=supersecretkey123
command: sh -c "npm install && npm start"
# ... 其他服務 ...
這個設定將建立一個新的 internal-api
服務,它將模擬我們的內部敏感 API。
接下來,我們需要建立 internal-api
目錄,並在其中建立必要的檔案:
在 internal-api/Dockerfile
中:
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD [ "node", "server.js" ]
在 internal-api/package.json
中:
{
"name": "internal-api",
"version": "1.0.0",
"description": "Internal API for SSRF demonstration",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.17.1"
}
}
在 internal-api/server.js
中:
const express = require('express');
const app = express();
const port = 4000;
const sensitiveData = {
admin: {
username: 'admin',
password: 'super_secret_password'
},
secretKey: process.env.SECRET_KEY || 'default_secret_key'
};
const localOnly = (req, res, next) => {
const ip = req.ip.replace(/^::ffff:/, '');
if (ip === '127.0.0.1' || ip.startsWith('172.') || ip.startsWith('192.168.')) {
next();
} else {
res.status(403).send('Access denied');
}
};
app.use(localOnly);
app.get('/api/sensitive', (req, res) => {
res.json(sensitiveData);
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
app.listen(port, () => {
console.log(`Internal API running on port ${port}`);
});
這個內部 API 包含了敏感資料,並試圖透過 IP 位址限制來保護自己。
現在,我們需要更新主應用程式以建立 SSRF 漏洞:
在 web/package.json
中新增 axios 相依套件:
"dependencies": {
// ... 其他相依套件 ...
"axios": "^0.21.1"
}
在 web/server.js
中新增新的路由:
const axios = require('axios');
// ... 其他程式碼 ...
app.use(bodyParser.json());
app.get('/fetch', async (req, res) => {
const { url } = req.query;
if (!url) {
return res.status(400).send('URL parameter is required');
}
try {
const response = await axios.get(url);
res.json(response.data);
} catch (error) {
console.error(error);
res.status(500).send('Error fetching URL');
}
});
這個新的 /fetch
路由允許客戶端指定一個 URL,伺服器將取得該 URL 的內容並回傳。這是一個典型的 SSRF 漏洞,因為它允許攻擊者存取內部網路資源。
現在,我們可以測試 SSRF 漏洞:
docker-compose up --build
http://nodelab.feifei.tw/fetch?url=http://internal-api:4000/health
curl "http://localhost:3000/fetch?url=http://internal-api:4000/api/sensitive"
http://nodelab.feifei.tw/fetch?url=http://internal-api:4000/api/sensitive
如果一切設定正確,這個請求應該能夠成功取得內部 API 的敏感資料。
透過這個實作,我們建立了一個包含 SSRF 漏洞的環境,以及一個模擬內部敏感 API 的服務。這個設定允許我們:
在實際的開發中,我們應該實施更嚴格的安全措施,如 URL 白名單、更嚴格的輸入驗證,以及網路分段等,以防止 SSRF 攻擊。
攻擊者可能利用 SSRF 執行更複雜的攻擊,例如:
存取雲端服務 memta 資料:
http://169.254.169.254/latest/meta-data/
連接埠掃描:
http://internal-server:22
讀取本機檔案:
file:///etc/passwd
這些攻擊可能導致敏感資訊洩露、未授權存取,甚至遠端程式碼執行。
要防止 SSRF 攻擊,可以採取以下措施:
const { URL } = require('url');
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch (err) {
return false;
}
}
app.get('/fetch', async (req, res) => {
const { url } = req.query;
if (!isValidUrl(url)) {
return res.status(400).send('Invalid URL');
}
// 繼續處理請求...
});
const allowedHosts = ['api.example.com', 'data.example.com'];
app.get('/fetch', async (req, res) => {
const { url } = req.query;
const parsedUrl = new URL(url);
if (!allowedHosts.includes(parsedUrl.hostname)) {
return res.status(403).send('Access to this host is not allowed');
}
// 繼續處理請求...
});
SSRF 是一種常見但危險的漏洞,它可能導致嚴重的安全問題。開發人員必須警惕這種威脅,並採取適當的防禦措施。透過實施嚴格的輸入驗證、使用白名單、限制協定,以及遵循最小權限原則,可以顯著降低 SSRF 攻擊的風險。
持續的安全稽核和更新是保護應用程式免受 SSRF 和其他新興威脅的關鍵。隨著攻擊技術的不斷演進,保持警惕和適應新的防禦策略至關重要。
SSRF 攻擊的主要目標是什麼?
A) 客戶端瀏覽器
B) 伺服器端應用程式
C) 資料庫
D) 防火牆
答案:B
解釋:SSRF 攻擊主要針對伺服器端應用程式,利用其代表攻擊者發送請求的能力。
下列哪項不是 SSRF 攻擊可能造成的後果?
A) 存取內部網路資源
B) 洩露敏感資訊
C) 直接修改客戶端程式碼
D) 繞過防火牆限制
答案:C
解釋:SSRF 攻擊發生在伺服器端,不能直接修改客戶端程式碼。其他選項都是 SSRF 可能導致的後果。
防止 SSRF 攻擊的最佳實作是什麼?
A) 禁用所有外部請求
B) 只使用 HTTPS
C) 實施嚴格的 URL 驗證和白名單
D) 增加伺服器的處理能力
答案:C
解釋:實施嚴格的 URL 驗證和使用白名單是防止 SSRF 攻擊的最有效方法。
在 Node.js 中,哪個模組常被用來發起可能導致 SSRF 的請求?
A) fs
B) http
C) axios
D) path
答案:C
解釋:axios 是一個流行的 HTTP 客戶端函式庫,常用於發起網路請求,如果使用不當,可能導致 SSRF 漏洞。
SSRF 攻擊者可能試圖存取以下哪個位址來取得雲端執行個體的 matadata 資料?
A) 8.8.8.8
B) 127.0.0.1
C) 169.254.169.254
D) 192.168.1.1
答案:C
解釋:169.254.169.254 是許多雲端服務提供商用於儲存執行個體 matadata 資料的 IP 位址,攻擊者可能試圖透過 SSRF 存取此位址以取得敏感資訊。